<!--suppress JSValidateTypes -->
<!--此组件用于表格自动合并单元格,自动排序,不可直接点击表格修改数据，无法触发相关事件，修改数据请使用弹出侧边或者对话框的方式---->
<template lang="pug">
q-table(v-model:selected="selected" :columns="columns" :rows="rowsOrder"  :row-key="rowKey" ref="table"
  table-header-style="position: sticky; top: 0; z-index: 2;"   :filter="search_text_trim" dense
table-header-class="bg-blue-1 text-subtitle"
  separator="cell" :rows-per-page-options="[0]" :style="tableStyle")
  // 自定义加载中样式
  template(v-if="rows?.length>0" v-slot:loading)
    q-inner-loading(showing color="primary" class="z-top")
      q-spinner-hourglass(color="green-6" size="2em")
      div(class="text-green-6") 加载中...
  // 自定义表头
  template(v-slot:header="props")
    q-tr(v-if="!mergeHeader" class="bg-blue-1 text-subtitle")
      q-th(v-for="col in props.cols" :key="col.name" :props="props" :col="col" style="position: sticky; top: 0; z-index: 2;")
        div(class="q-gutter-sm")
          div(class="text-center")
            span(v-if="!col.not_required && !readOnly && col.field!='operation'" class="text-red-6") *
            span {{ col.label }}
    template(v-else v-for="(row, index) in mergeHeaderOptionRet" :key="index")
      q-tr( class="bg-blue-1 text-subtitle")
        q-th(v-for="col in row" :key="col.name"
          :style="'border-left: 1px solid rgba(0,0,0,0.14);padding-left: 8px;max-width: 300px;position: sticky; top:' + (index*28).toString() + 'px; z-index: 2;'"
          :colspan="col.col_span" :rowspan="col.row_span")
          div(class="q-gutter-sm")
            div(class="text-center")
              span {{ col.label }}
  // 自定义右上角配置
  template(v-slot:top-right="props")
    slot(name="top-right-extra" :props="props")
    // 模板
    q-btn(color="primary" class="q-mx-xs" v-if="canDownloadTemplate" dense label="下载模板"
      @click="downloadTemplate")
    ticket-uploader(v-model="importFileData"
      ref="importFile"
      v-show="false"
      :max-file-size="uploadLimit.maxFileSize"
      :accept="uploadLimit.accept"
      :xq_number="uploadParams.xq_number"
      :ticket_id="uploadParams.ticket_id"
      :state_id="uploadParams.state_id"
      :dir_type="uploadParams.dir_type"
      max-files="1"
      is-import
      @after-uploaded="fileAdded")
    q-btn(color="primary" class="q-mx-xs" v-if="canImport && !readOnly" dense label="导入数据" @click="importTable")
    template(v-if="canExport")
      q-btn(color="primary" class="q-mx-xs" v-if="customExport" dense label="导出" @click="customExportHandle")
      q-btn(color="primary" class="q-mx-xs" v-else dense label="导出" @click="exportTable(table.filteredSortedRows)" )
    q-btn(color="primary" class="q-mx-xs" v-if="canAdd && !readOnly" dense label="新增" @click="addRow" )
    //全局搜索
    q-input(v-model="search_text" v-if="canSearch" class="q-mx-md" dense debounce="500" placeholder="全局搜索")
      template(v-slot:append)
        q-icon(name="search")
    // 专注模式
    q-btn(flat round dense :icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
      @click="toggleFullscreen(props)"  v-if="fullScreen")
      q-tooltip {{ props.inFullscreen ? '退出专注模式' : '专注模式' }}
  template(v-slot:body-cell="props")
    q-td(v-if="rowsMergeIndex.hasOwnProperty(props.col.field) && rowsMergeIndex[props.col.field].includes(props.pageIndex)"
      :style="'border-left: 1px solid rgba(0,0,0,0.14);padding-left: 8px;max-width: 300px;'+props.col.style?props.col.style:''"
      :rowspan="rowsMergeRowspan[props.col.field][props.pageIndex]" :props="props" :class="'text-center q-pa-xs ' + cellClassName(props)")
      div(class="q-gutter-sm" v-if="props.col.field=='operation'")
        slot(name="extra_operation" :props="props")
      div(class="q-gutter-sm" v-else-if="props.col.type=='buttons'"
        :style="'background: white; min-width: 100px; max-width: 200px;'+props.col.style?props.col.style:''")
        slot(:name="props.col.field" :props="props")
      div(v-else class="full-width text-center ellipsis-3-lines" style="white-space: normal;") {{ props.value }}
        q-tooltip(max-width="50vw" v-if="props.col.tooltip") {{ props.value }}
    q-td(v-else-if="!rowsMergeIndex.hasOwnProperty(props.col.field)" :class="'text-center ' + cellClassName(props)"
      :style="'border-left: 1px solid rgba(0,0,0,0.14);padding-left: 8px;max-width: 300px;'+props.col.style?props.col.style:''" )
      div(class="q-gutter-sm" v-if="props.col.field=='operation'")
        slot(name="extra_operation" :props="props")
      div(class="q-gutter-sm" v-else-if="props.col.type=='buttons'"
        :style="'background: white; min-width: 100px; max-width: 200px;'+props.col.style?props.col.style:''")
        slot(:name="props.col.field" :props="props")
      q-field(v-model="props.value" v-else-if="!props.col.not_required && !readOnly"
        hide-bottom-space dense borderless  class="full-width"
        :rules="[val=> {return !!val || is.number(val) || '不能为空'}]"
        lazy-rules)
        template(v-slot:control)
          div(class="full-width text-center ellipsis-3-lines"
            style="white-space: normal;") {{ props.value }}
            q-tooltip(max-width="50vw" v-if="props.col.tooltip") {{ props.value }}
          q-popup-edit(v-model="props.row[props.col.field]" :title="`编辑 ${props.col.label}`"
            @save="saveData(props)"  buttons auto-save v-slot="scope" v-if="!!props.col.editable && !readOnly" label-set="确认" )
            q-select(v-model="scope.value" v-if="props.col.type=='select'"
              dense :options="props.col.options")
            time-range(v-else-if="props.col.type=='time_range'" v-model="scope.value")
            number-input(v-model="scope.value"  v-else-if="props.col.type=='number'"
              :dig-num="props.col.dig_num" fill-mask="0"
              dense hide-bottom-space filled clearable)
            div(v-else-if="props.col.type=='datetime'" class="q-gutter-xs  row items-start")
              q-date(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss")
              q-time(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss" format24h )
            opt-select(v-model="scope.value" v-else-if="props.col.type=='user-select'"
              filterable
              transition-show="scale" transition-hide="scale"
              dense hide-bottom-space clearable
              filled  options-dense
              :options="userList")
            opt-select(v-model="scope.value" v-else-if="props.col.type=='system-select'"
              filterable
              transition-show="scale" transition-hide="scale"
              dense hide-bottom-space clearable
              filled  options-dense
              :options="systemList")
            q-input(v-model="scope.value" v-else-if="props.col.type=='text-area'" @keyup.enter="scope.set" type="textarea"
              :maxlength="props.col.maxlength" input-style="width:500px"
              dense autofocus counter)
            q-input(v-model="scope.value" v-else
            dense autofocus counter)
      template(v-else)
        div(class="full-width text-center ellipsis-3-lines"
          style="white-space: normal;") {{ props.value }}
          q-tooltip(max-width="50vw" v-if="props.col.tooltip") {{ props.value }}
        q-popup-edit(v-model="props.row[props.col.field]" :title="`编辑 ${props.col.label}`" @save="saveData(props)"
          buttons auto-save v-slot="scope" v-if="!!props.col.editable && !readOnly" label-set="确认" )
          q-select(v-model="scope.value" v-if="props.col.type=='select'"
            dense :options="props.col.options")
          time-range(v-else-if="props.col.type=='time_range'" v-model="scope.value")
          div(v-else-if="props.col.type=='datetime'")
            q-date(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss")
            q-time(v-model="scope.value" mask="YYYY-MM-DD HH:mm:ss" format24h )
          opt-select(v-model="scope.value" v-else-if="props.col.type=='user-select'"
            filterable
            transition-show="scale" transition-hide="scale"
            dense hide-bottom-space clearable
            filled  options-dense
            :options="userList")
          q-input(v-model="scope.value" v-else-if="props.col.type=='text-area'" @keyup.enter="scope.set" type="textarea"
            :maxlength="props.col.maxlength" input-style="width:500px"
            dense autofocus counter)
          opt-select(v-model="scope.value" v-else-if="props.col.type=='system-select'"
            filterable
            transition-show="scale" transition-hide="scale"
            dense hide-bottom-space clearable
            filled  options-dense
            :options="systemList")
          q-input(v-model="scope.value" v-else
          dense autofocus counter)
  template(v-if="summary && table?.filteredSortedRows?.length>0" v-slot:bottom-row="{cols}")
    q-tr
      q-td(v-for="(col, i) in cols" class="bg-blue-1 text-center"
        style="border-left: 1px solid rgba(0,0,0,0.14);padding-left: 8px; border-top: 1px solid rgba(0,0,0,0.14); max-width:300px")
        div() {{ summaryOption[col.field].format && summaryData.hasOwnProperty(col.field) ? col.format(summaryData[col.field]) : summaryData[col.field] }}

</template>

<script setup>
import TimeRange from "./TimeRange.vue";

const _doc = "此组件用于表格自动合并单元格,请随意扩展"

import {computed, ref, watch, nextTick} from "vue";
import {exportFile, extend, is, date, debounce} from 'quasar'
import OptSelect from "./OptSelect.vue";
import NumberInput from "./NumberInput.vue";
import TicketUploader from "~/components/tickets/TicketUploader.vue";
import NormalNumberInput from "~/components/optimaztion/NormalNumberInput.vue";

const table = ref(null)

const props = defineProps({
  summary: {
    type: Boolean,
    default: false
  },
  summaryOption: {
    type: Object,
    default: {}
  },
  mergeFields: {
    type: Array,
    default: []
  },
  mergeHeader: {
    type: Boolean,
    default: false
  },
  mergeHeaderOption: {
    type: Array,
    default: []
  },
  columns: {
    type: Array,
    default: () => []
  },
  rows: {
    type: Array,
    default: () => []
  },
  userDict: {
    type: Object,
    default: () => {
    }
  },
  userList: {
    type: Array,
    default: () => []
  },
  systemDict: {
    type: Object,
    default: () => {
    }
  },
  systemList: {
    type: Array,
    default: () => []
  },
  canExport: {
    type: Boolean,
    default: true
  },
  customExport: {
    type: Boolean,
    default: false
  },
  canSearch: {
    type: Boolean,
    default: true
  },
  fullScreen: {
    type: Boolean,
    default: false
  },
  exportName: {
    type: String,
    default: date.formatDate(Date.now(), 'YYYYMMDDHHmmss')
  },
  readOnly: {type: Boolean, default: false},
  canAdd: {
    type: Boolean,
    default: false
  },
  canImport: {type: Boolean, default: false},
  canDownloadTemplate: {type: Boolean, default: false},
  uploadLimit: {
    type: Object,
    default: {
      accept: ".xlsx,.xls",
      maxFileSize: "1024*1024*100"
    }
  },
  uploadParams: {
    type: Object,
    default: {}
  },
  rowKey: {
    type: String,
    default: 'no_key_set'
  },
  isReversed: {type: Boolean, default: false},
  tableStyle: {
    type: String,
    default: 'max-height: 60vh;'
  }
})

// onMounted(() => {
//   // 生成 rowsMergeRowspan 和 rowsMergeIndex
//   let result = extend(true, [], props.rows)
//   rowsMergeAction(props.mergeFields, result)
// })

/*
*  +----------------------------+
*  +         datas             +
*  +----------------------------+
*/

const rowsMergeRowspan = ref({})
const rowsMergeIndex = ref({})
const selected = ref([])
const loading = ref(false)
const search_text = ref('')

const emit = defineEmits([
  'addRow', 'fileAdded', 'downloadTemplate', 'saveData', 'update:tableStyle'
])

const addRow = () => {
  emit('addRow')
}
/*
*  +----------------------------+
*  +         computed           +
*  +----------------------------+
*/
const summaryData = computed(() => {
  if (!props.summary) return {}
  if (!table.value) return {}
  let result = {}
  let _tmp = {}
  for (let i = 0; i < table.value.filteredSortedRows.length; i++) {
    let row = table.value.filteredSortedRows[i]
    for (let column in row) {
      if (_tmp.hasOwnProperty(column)) {
        _tmp[column].push(row[column])
      } else {
        _tmp[column] = [row[column]]
      }
    }
  }
  for (let column in props.summaryOption) {
    let _option = props.summaryOption[column]
    if (_option.type == 'constant') {
      result[column] = _option.value
    }
    if (_option.type == 'sum') {
      if (_tmp.hasOwnProperty(column)) {
        result[column] = _tmp[column].reduce((a, b) => {
          if (!is.number(b)) return a
          return a + b
        }, 0)
      }
    }
    if (_option.type == 'avg') {
      if (_tmp.hasOwnProperty(column)) {
        result[column] = _tmp[column].reduce((a, b) => {
          if (!is.number(b)) return a
          return a + b
        }, 0) / _tmp[column].length
      }
    }
  }
  return result
})

const rowsOrder = computed(() => {
  // 将 props.rows 按照 mergeFields 依次排序
  let result = extend(true, [], props.rows)
  let fields = extend(true, [], props.mergeFields)
  // 优先级从高到低, 需要反转
  fields.reverse()
  if (props.isReversed) return result;
  for (let i = 0; i < fields.length; i++) {
    const field = fields[i]
    if (field == 'operation') {
      continue
    }
    result.sort((a, b) => {
      if (is.number(a[field])) {
        return a[field] - b[field]
      }
      if (!a[field]) {
        return 1
      }
      if (!b[field]) {
        return -1
      }
      return a[field].localeCompare(b[field])
    })
  }
  return result
})

const search_text_trim = computed(() => search_text.value.trim())


/*
*  +----------------------------+
*  +         methods             +
*  +----------------------------+
*/

const init = () => {
  // 生成 rowsMergeRowspan 和 rowsMergeIndex
  let result = extend(true, [], table.value.filteredSortedRows)
  rowsMergeAction(props.mergeFields, result)
}

const saveData = (p) => {
  emit('saveData', p)
}

const toggleFullscreen = (p) => {
  if (p.inFullscreen) {
    emit('update:tableStyle', 'max-height: 60vh;')
  } else {
    emit('update:tableStyle', 'max-height: 100vh;')
  }
  p.toggleFullscreen()
}

const cellClassName = (props) => {
  if (!props.row.hasOwnProperty('cellClassName')) return ''
  if (!props.row.cellClassName.hasOwnProperty(props.col.field)) return ''
  return props.row.cellClassName[props.col.field]
}


const rowsMergeAction = (merge_keys, rows, index = 0) => {
  if (merge_keys.length === index) return rows
  let merge_key = merge_keys[index]
  if (merge_key == 'operation') return rows
  let _tmp_list = []
  let current_key = 'xxxxxxxxxx'
  for (let i = 0; i < rows.length; i++) {
    let _row = rows[i]
    if (current_key == _row[merge_key]) {
      _tmp_list[_tmp_list.length - 1].push(_row)
    } else {
      current_key = _row[merge_key]
      _tmp_list.push([_row])
    }
  }
  for (let i = 0; i < _tmp_list.length; i++) {
    let _rows = _tmp_list[i]
    if (rowsMergeRowspan.value.hasOwnProperty(merge_key)) {
      let index_len = rowsMergeIndex.value[merge_key][rowsMergeIndex.value[merge_key].length - 1]
      rowsMergeIndex.value[merge_key].push(index_len + _rows.length)
      rowsMergeRowspan.value[merge_key][index_len] = _rows.length
    } else {
      rowsMergeRowspan.value[merge_key] = {0: _rows.length}
      rowsMergeIndex.value[merge_key] = [0, _rows.length]
    }
    rowsMergeAction(merge_keys, _rows, index + 1)
  }
  if (props.mergeFields.includes('operation')) {
    rowsMergeRowspan.value['operation'] = rowsMergeRowspan.value[props.mergeFields[0]]
    rowsMergeIndex.value['operation'] = rowsMergeIndex.value[props.mergeFields[0]]
  }
}

const headerMergeRow = (header_options, return_options) => {
  let tmp_children = []
  let has_children = false
  for (let i = 0; i < header_options.length; i++) {
    let _header = header_options[i]
    if (_header.children) {
      has_children = true
      tmp_children.push(..._header.children)
    }
  }
  return_options.push(header_options)
  if (has_children) {
    for (let i = 0; i < return_options.length; i++) {
      let children = return_options[i]
      for (let j = 0; j < children.length; j++) {
        let child = children[j]
        if (!child.hasOwnProperty('children')) {
          if (child.hasOwnProperty('row_span')) {
            child.row_span += 1
          } else {
            child.row_span = 2
          }
        } else {
          child.row_span = 1
        }
      }
    }
    headerMergeRow(tmp_children, return_options)
  }
  return return_options
}

const headerMergeCol = (return_options) => {
  for (let i = 0; i < return_options.length; i++) {
    let children = return_options[i]
    for (let j = 0; j < children.length; j++) {
      let child = children[j]
      if (child.hasOwnProperty('children')) {
        headerMergeCol([child.children])
        // chidlren 的 col_span 求和
        child['col_span'] = child.children.reduce((a, b) => {
          return a + b.col_span
        }, 0)
      } else {
        child.col_span = 1
      }
    }
  }
}

const mergeHeaderOptionRet = computed(() => {
  let return_options = []
  let header_options = props.mergeHeaderOption
  return_options = headerMergeRow(header_options, return_options)
  headerMergeCol(return_options)
  return return_options
})


const wrapCsvValue = (val, formatFn, row) => {
  let formatted = formatFn !== void 0
      ? formatFn(val, row)
      : val

  formatted = formatted === void 0 || formatted === null
      ? ''
      : String(formatted)

  formatted = formatted.split('"').join('""')
  /**
   * Excel accepts \n and \r in strings, but some other CSV parsers do not
   * Uncomment the next two lines to escape new lines
   */
  // .split('\n').join('\\n')
  // .split('\r').join('\\r')

  return `"${formatted}"`
}

const exportTable = (datas) => {
  // naive encoding to csv format
  const content = [props.columns.filter(c => c.field != 'operation').map(col => wrapCsvValue(col.label))].concat(
      datas.map(row => props.columns.map(col => wrapCsvValue(
          typeof col.field === 'function'
              ? col.field(row)
              : row[col.field === void 0 ? col.name : col.field],
          col.format,
          row
      )).join(','))
  ).join('\r\n')
  const status = exportFile(
      props.exportName + '.csv',
      content,
      {
        encoding: 'utf-8', byteOrderMark: '\uFEFF',
        mimeType: 'text/csv;charset=UTF-8;'
      }
  )

  if (status !== true) {
    $q.notify({
      message: '文件导出失败',
      color: 'negative',
      icon: 'warning',
      position: 'top'
    })
  }
}

const importFile = ref(null)
const importFileData = ref([])

const importTable = () => {
  importFile.value.pickFiles()
}
const fileAdded = () => {
  emit('fileAdded', importFileData)
}

const downloadTemplate = () => {
  emit('downloadTemplate')
}

const customExportHandle = () => {
  emit('customExport')
}


/*
*  +----------------------------+
*  +         watch             +
*  +----------------------------+
*/

watch(() => table.value?.filteredSortedRows, () => {
  if (!table.value || table.value.filteredSortedRows.length == 0) return
  rowsMergeRowspan.value = {}
  rowsMergeIndex.value = {}
  init()
}, {deep: true, immediate: true})


</script>

<style scoped>

</style>